﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iNet.Media
{
	/// <summary>
	/// 表示一個地理位置。
	/// </summary>
	public class GeoCoordinate
	{
		#region 常數
		const string _LocationStringFormat = "{0:0.00000} {1}";
		#endregion
		#region 欄位
		double _Altitude;
		double _Latitude;
		double _Longitude;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 GeoCoordinate 執行個體。
		/// </summary>
		public GeoCoordinate()
			: this(0, 0, double.NaN)
		{ }
		/// <summary>
		/// 初始化新的 GeoCoordinate 執行個體。
		/// </summary>
		/// <param name="latitude">緯度，正值表示北緯，負值表示南緯。範圍為 -90 至 90，Double.NaN 表示沒有緯度資訊。</param>
		/// <param name="longitude">經度，正值表示東經，負值表示西經。範圍為 -180 至 180，Double.NaN 表示沒有經度資訊。</param>
		/// <exception cref="ArgumentOutOfRangeException">緯度或經度超出範圍。</exception>
		public GeoCoordinate(double latitude, double longitude)
			: this(latitude, longitude, double.NaN)
		{ }
		/// <summary>
		/// 初始化新的 GeoCoordinate 執行個體。
		/// </summary>
		/// <param name="latitude">緯度，正值表示北緯，負值表示南緯。範圍為 -90 至 90，Double.NaN 表示沒有緯度資訊。</param>
		/// <param name="longitude">經度，正值表示東經，負值表示西經。範圍為 -180 至 180，Double.NaN 表示沒有經度資訊。</param>
		/// <param name="altitude">海拔高度，單位為公尺。Double.NaN 表示沒有高度資訊。</param>
		/// <exception cref="ArgumentOutOfRangeException">緯度或經度超出範圍。</exception>
		public GeoCoordinate(double latitude, double longitude, double altitude)
		{
			//確認參數
			if (!double.IsNaN(latitude) && (latitude > 90 || latitude < -90))
				throw new ArgumentOutOfRangeException();
			if (!double.IsNaN(longitude) && (longitude > 180 || longitude < -180))
				throw new ArgumentOutOfRangeException();

			//初始化欄位
			_Altitude = altitude;
			_Latitude = latitude;
			_Longitude = longitude;
		}
		#endregion
		#region Altitude
		/// <summary>
		/// 取得或設定海拔高度，單位為公尺。Double.NaN 表示沒有高度資訊。
		/// </summary>
		public double Altitude
		{
			get
			{
				return _Altitude;
			}
			set
			{
				_Altitude = value;
			}
		}
		#endregion
		#region Equals
		/// <summary>
		/// 和指定的物件比較是否相等。
		/// </summary>
		/// <param name="obj">指定的物件。</param>
		/// <returns>True 表示物件相等，False 表示不相等。</returns>
		public override bool Equals(object obj)
		{
			GeoCoordinate location = (obj as GeoCoordinate);
			if (location != null)
			{
				//比較高度
				if (!double.IsNaN(_Altitude) && !double.IsNaN(location._Altitude))
				{
					if (_Altitude != location._Altitude)
						return false;
				}
				else if (double.IsNaN(_Altitude) != double.IsNaN(location._Altitude))
					return false;

				//比較緯度
				if (!double.IsNaN(_Latitude) && !double.IsNaN(location._Latitude))
				{
					if (_Latitude != location._Latitude)
						return false;
				}
				else if (double.IsNaN(_Latitude) != double.IsNaN(location._Latitude))
					return false;

				//比較經度
				if (!double.IsNaN(_Longitude) && !double.IsNaN(location._Longitude))
				{
					if (_Longitude != location._Longitude)
						return false;
				}
				else if (double.IsNaN(_Longitude) != double.IsNaN(location._Longitude))
					return false;

				//完成
				return true;
			}
			return false;
		}
		#endregion
		#region GetHashCode
		/// <summary>
		/// 取得雜湊值。
		/// </summary>
		/// <returns>表示此地理位置的雜湊值。</returns>
		public override int GetHashCode()
		{
			int alt = (double.IsNaN(_Altitude) ? 0 : (int)_Altitude);
			int lat = (double.IsNaN(_Latitude) ? 0 : (int)_Latitude);
			int lng = (double.IsNaN(_Longitude) ? 0 : (int)_Longitude);
			return (alt << 24) | ((lat & 0xFFF) << 12) | (lng & 0xFFF);
		}
		#endregion
		#region IsUnknown
		/// <summary>
		/// 取得此結構是否不含緯度或經度資訊。
		/// </summary>
		public bool IsUnknown
		{
			get
			{
				return (double.IsNaN(_Latitude) || double.IsNaN(_Longitude));
			}
		}
		#endregion
		#region Latitude
		/// <summary>
		/// 取得或設定緯度，正值表示北緯，負值表示南緯。範圍為 -90 至 90，Double.NaN 表示沒有緯度資訊。
		/// </summary>
		public double Latitude
		{
			get
			{
				return _Latitude;
			}
			set
			{
				if (!double.IsNaN(value) && (value > 90 || value < -90))
					throw new ArgumentOutOfRangeException();
				_Latitude = value;
			}
		}
		#endregion
		#region Longitude
		/// <summary>
		/// 取得或設定經度，正值表示東經，負值表示西經。範圍為 -180 至 180，Double.NaN 表示沒有經度資訊。
		/// </summary>
		public double Longitude
		{
			get
			{
				return _Longitude;
			}
			set
			{
				if (!double.IsNaN(value) && (value > 180 || value < -180))
					throw new ArgumentOutOfRangeException();
				_Longitude = value;
			}
		}
		#endregion
		#region ToString
		/// <summary>
		/// 取得表示地理位置的字串。
		/// </summary>
		/// <returns>表示地理位置的字串。</returns>
		public override string ToString()
		{
			if (double.IsNaN(_Latitude) && double.IsNaN(_Longitude))
				return "Unknown";
			StringBuilder buffer = new StringBuilder("(");
			if (!double.IsNaN(_Latitude))
			{
				if (_Latitude >= 0)
					buffer.AppendFormat(_LocationStringFormat, _Latitude, 'N');
				else
					buffer.AppendFormat(_LocationStringFormat, -_Latitude, 'S');
			}
			buffer.Append(" , ");
			if (!double.IsNaN(_Longitude))
			{
				if (_Longitude >= 0)
					buffer.AppendFormat(_LocationStringFormat, _Longitude, 'E');
				else
					buffer.AppendFormat(_LocationStringFormat, -_Longitude, 'W');
			}
			buffer.Append(')');
			return buffer.ToString();
		}
		#endregion
	}
}
